#include "client.h"

char* RecvFile(int connFd){
   //0. 接收标志位，判断发送端文件是否存在
   int flag;
   int ret;
   ret = recv(connFd, &flag, 4, MSG_WAITALL);
   if(ret < 0){
       perror("recv");
       return "recv error.\n";
   }
   if(0 == ret){//连接断开
       printf("conn has closed.\n");
       return "conn closed.\n";
   }
   if(flag == 0){//标志位为0文件不存在，直接返回
       printf("file is not existed.\n");
       return "file is not existed.\n";
   }

   //1. 接收文件名长度
   int length;
   ret = recv(connFd, &length, 4, MSG_WAITALL);
   if(ret < 0){
       perror("recv");
       return "recv error.\n";
   }
   if(0 == ret){//连接断开
       printf("conn has closed.\n");
       return "conn closed.\n";
   }
   printf(">> 1. fileName length: %d\n.", length);

   char recvBuff[BUFFSIZE];//应用层缓冲区
   //2. 接收文件名
   memset(recvBuff, 0, BUFFSIZE);
   ret = recv(connFd, recvBuff, length, MSG_WAITALL);
   if(ret < 0){
       perror("recv");
       return "recv error.\n";
   }
   if(0 == ret){//连接断开
       printf("conn has closed.\n");
       return "conn closed.\n";
   }
   printf(">> 2. fileName: %s\n.",recvBuff);

   //3. 接收文件大小
   off_t fileSize;
   ret = recv(connFd, &fileSize, sizeof(off_t), MSG_WAITALL);
   if(ret < 0){
        perror("recv");
        return "recv error.\n";
   }
   if(0 == ret){//连接断开
       printf("conn has closed.\n");
       return "conn closed.\n";
   }
   printf(">> 3. fileSize: %ld\n.", fileSize);

   //4. 在本地打开或者创建文件
   int fileFd = open(recvBuff, O_RDWR | O_CREAT, 0666);
   if(fileFd < 0){
       perror("open");
       return "open error.\n";
   }

   //5. 获取文件信息，确定偏移量及接收文件长度
   struct stat st;
   if(fstat(fileFd, &st) < 0){
       perror("fstat");
       return "fstat error.\n";
   }
   off_t offSize = st.st_size;//记录本地文件长度，实现断点续传
   if(send(connFd, &offSize, sizeof(off_t), MSG_WAITALL) < 0){//把本地文件长度发送给发送方
       perror("send");
       return "send error.\n";
   }
   
   //实现显示文件下载进度
   off_t slice = fileSize / 100;
   off_t lastSize = offSize;

   off_t loadSize = offSize;//文件已下载大小
   printf("download percent: %5.2lf%%\n", (double)loadSize / fileSize * 100);//显示文件已下载进度

   if(fileSize > _100M){//大文件(>100M)用零拷贝技术mmap接收
       while(loadSize < fileSize){
           off_t cpSize = 0;//每次要传输的文件大小
           if(fileSize - loadSize < _1M){
               cpSize = fileSize - loadSize;
           }else{
               cpSize = _1M;
           }
           ftruncate(fileFd, loadSize + cpSize);//每次传输前设置好文件大小
           char* pbuf = (char*)mmap(NULL, cpSize, PROT_READ | PROT_WRITE, MAP_SHARED, fileFd, loadSize);
           ret = recv(connFd, pbuf, cpSize, MSG_WAITALL);//接收文件内容
           if(ret < 0){
               perror("recv");
               return "recv error.\n";
           }
           if(0 == ret){//连接断开
               printf("conn has closed.\n");
               return "conn closed.\n";
           }
           loadSize += ret;//更新已下载文件大小
           if(loadSize - lastSize > slice){
               printf("download percent: %5.2lf%%\n", (double)loadSize / fileSize * 100);
               lastSize = loadSize;
           }
           munmap(pbuf, cpSize);
       }
   }else{//小文件
       if(lseek(fileFd, offSize, SEEK_CUR) < 0){//将文件指针偏移至文件末尾
           perror("lseek");
           return "lseek error.\n";
       }
       while(loadSize < fileSize){
           memset(recvBuff, 0, sizeof(recvBuff));
           ret = recv(connFd, recvBuff, length, MSG_WAITALL);//接收文件内容
           if(ret < 0){
               perror("recv");
               return "recv error.\n";
           }
           if(0 == ret){//连接断开
               printf("conn has closed.\n");
               return "conn closed.\n";
           }
           if(write(fileFd, recvBuff, ret) < 0){//写入本地文件
               perror("recv");
               return "write error.\n";
           }
           loadSize += ret;//更新已下载文件大小
           if(loadSize - lastSize > slice){
               printf("download percent: %5.2lf%%\n", (double)loadSize / fileSize * 100);
               lastSize = loadSize;
           }
       }
   }
   printf(">> 4. file recv over!\n");
   close(fileFd);
   return "recv success!\n";
}
